#ifndef __virt_gpio_kenny_dkfjiwoo3w3__
#define __virt_gpio_kenny_dkfjiwoo3w3__

#include <atomic>

#include <hude/base/base_type.hpp>
#include <hude/safe/crc.hpp>
#include <hude/utils/serialport.hpp>
#include <hude/utils/parses.hpp>
#include <hude/aio/context.hpp>

namespace ha
{
    namespace brick
    {
        /*********************************************************************
         * @brief _pkg_gpio_head_t
         */
        struct _pkg_gpio_head_t
        {
            enum : size_t { emSIZ_PERFIX = 4U, emSIZ_HEAD = 8U };

            union perfix_t
            {
                uint32_t num;
                char str[4];
            };

            perfix_t perfix;

            uint8_t  ver;
            uint8_t  crc;
            uint16_t siz;
        };

        /*********************************************************************
         * @brief gpio
         */
        struct _gpio_t
        {
            enum direction_t : uint8_t { emDIN, emDOUT };

            enum value_t : uint8_t { emVL, emVH };

            enum cmd_t : int
            {
                emCMD_NULL,
                emCMD_OPENED,
                emCMD_OPEN,
                emCMD_CLOSE,
                emCMD_GET_DIR,
                emCMD_SET_DIR,
                emCMD_GET,
                emCMD_SET,
                emCMD_MAX
            };

            union ident_t
            {
                struct
                {
                    uint8_t group;
                    uint8_t index;
                };

                uint16_t id;
            };

            ident_t  ident;

            uint16_t result : 4;
            uint16_t command : 4;
            uint16_t value : 1;
            uint16_t values : 3;
            uint16_t direction : 1;
            uint16_t flags : 3;
        };

        /*********************************************************************
         * @brief _pkg_gpio_t
         */
        struct _pkg_gpio_t
        {
            enum : size_t { emSIZ_BODY = 4U, emSIZ_PKG = _pkg_gpio_head_t::emSIZ_HEAD + emSIZ_BODY };

            _pkg_gpio_head_t head;

            union
            {
                uint32_t gpio_all;
                _gpio_t  gpio;
            };
        };

        /*********************************************************************
         * @brief pkg_gpio_t
         */
        struct pkg_gpio_t :
            public _pkg_gpio_t
        {
            enum
            {
                emSIZ_HEAD   = _pkg_gpio_head_t::emSIZ_HEAD,
                emSIZ_PERFIX = _pkg_gpio_head_t::emSIZ_PERFIX,
                emSIZ_BODY   = _pkg_gpio_t::emSIZ_BODY,
                emSIZ_PKG    = _pkg_gpio_t::emSIZ_PKG,
                emSIZ_CRC    = _pkg_gpio_t::emSIZ_BODY + 2U,
            };

            using direction_t = _gpio_t::direction_t;
            using value_t     = _gpio_t::value_t;
            using cmd_t       = _gpio_t::cmd_t;
            using parses_t    = hude::utils::parses_t<>;

            /*********************************************************************
             * @brief 构造数据桢
             */
            pkg_gpio_t( uint8_t g, uint8_t i, value_t v = value_t::emVL, direction_t d = direction_t::emDIN,
                cmd_t c = cmd_t::emCMD_NULL, uint8_t f = 0U )
            {
                head = *((const _pkg_gpio_head_t*)this->perfix());
                gpio_all = 0U;

                gpio.ident.group = g;
                gpio.ident.index = i;
                gpio.value       = v;
                gpio.direction   = d;
                gpio.command     = c;
                gpio.flags       = f;

                head.siz = 4U;
                head.crc = get_crc();
            }

            /*********************************************************************
             * @brief 构造空数据桢
             */
            pkg_gpio_t() : pkg_gpio_t( 0U, 0U ) {}

            /*********************************************************************
             * @brief 计算当前数据桢的CRC
             */
            uint8_t get_crc() const
            {
                return hude::utils::crc_t< 8 >::calc( (const uint8_t*)&(head.siz), emSIZ_CRC );
            }

            /*********************************************************************
             * @brief 检查当前数据桢是否规范
             */
            bool check() const
            {
                auto p = (const _pkg_gpio_head_t*)( perfix() );

                if( head.perfix.num != p->perfix.num ) return false;
                if( head.ver != p->ver ) return false;

                return get_crc() == head.crc;
            }

            /*********************************************************************
             * @brief 获取标准包头
             */
            static const _pkg_gpio_head_t::perfix_t* perfix()
            {
                static const char s[] = "ver\000""1""\0\0";
                return (const _pkg_gpio_head_t::perfix_t*)(const void*)s;
            }

            /*********************************************************************
             * @brief 数据桢识别函数
             */
            static parses_t::citr_t scaner( parses_t::citr_t& b, parses_t::citr_t& e )
            {
                parses_t::citr_t i;

                for( i = b; i != e; ++i )
                {
                    if( *i == 'v' && ( e - i ) >= (int)_pkg_gpio_t::emSIZ_PKG )
                    {
                        pkg_gpio_t pkg;

                        memcpy( &pkg, &(*i), pkg_gpio_t::emSIZ_PKG );

                        if( pkg.check() )
                        {
                            b = i + pkg_gpio_t::emSIZ_PKG;
                            return i;
                        }
                    }
                }

                return b = i, e;
            };
        };

        /*********************************************************************
         * @brief gpio客户端实现
         */
        class gpio_client_t
        {
        public:
            using aio_t    = hude::aio::aio_t<>;
            using sport_t  = hude::utils::serialport_t;
            using parses_t = hude::utils::parses_t<>;
            using sync_t   = hude::utils::sync_observe_t< gpio_client_t, parses_t >;
            using dir_t    = _gpio_t::direction_t;

            friend class hude::utils::sync_observe_t< gpio_client_t, parses_t >;

        public:
            /*********************************************************************
             * @brief ...
             */
            gpio_client_t( const char* name = "/tmp/dev/ttyGPIO", int speed = 115200U ) :
                gpio_client_t( name, speed, hude::aio::context_t<>::aio() ) {}

            /*********************************************************************
             * @brief ...
             */
            gpio_client_t( const char* name, int speed, aio_t aio ) :
                aio_( aio ), name_( name ), speed_( speed ), parsing_( false )
            {
                if ( !init() ) llog_err( "<plugs>", "ERROR: init gpio client." );
            }

            /*********************************************************************
             * @brief ...
             */
            ~gpio_client_t() { deinit(); }

            /*********************************************************************
             * @brief 查询指定gpio是否已经打开
             */
            hude::optional_t< bool > enable( uint16_t index );

            /*********************************************************************
             * @brief 打开或关闭指定gpio
             */
            bool enable( uint16_t index, bool value );

            /*********************************************************************
             * @brief 设置指定gpio的方向
             */
            bool direction( uint16_t index, dir_t dir );

            /*********************************************************************
             * @brief 查询指定gpio的方向
             */
            hude::optional_t< dir_t > direction( uint16_t index );

            /*********************************************************************
             * @brief 查询指定gpio的电平
             */
            hude::optional_t< bool > value( uint16_t index );

            /*********************************************************************
             * @brief 设置指定gpio的电平
             */
            bool value( uint16_t index, bool level );

        private:
            /*********************************************************************
             * @brief 发送数据桢
             */
            bool send( const hude::buffer_t& buffer );

            /*********************************************************************
             * @brief 发送数据桢
             */
            inline bool operator()( const hude::buffer_t& buffer ) { return this->send( buffer ); }

            /*********************************************************************
             * @brief 初始化
             */
            bool init();

            /*********************************************************************
             * @brief 反初始化
             */
            void deinit();

            /*********************************************************************
             * @brief 解析缓冲中存在的所有数据
             */
            void parse();

            /*********************************************************************
             * @brief 完成一次同步请求与回应
             */
            bool request( const pkg_gpio_t& req, std::function< void( pkg_gpio_t& ) > cb, size_t timeout );

            /*********************************************************************
             * @brief 异步IO事件处理
             */
            void on_recv( uint32_t evts, hude::any_t& );

        private:
            gpio_client_t( gpio_client_t&& ) = delete;
            gpio_client_t( const gpio_client_t& ) = delete;
            gpio_client_t& operator=( const gpio_client_t& ) = delete;

        private:
            aio_t            aio_;
            sport_t          sport_;
            parses_t         parses_;
            std::string      name_;
            int              speed_;
            std::atomic_flag parsing_;

        };

        /*********************************************************************
         * @brief 异步IO事件处理
         */
        inline void gpio_client_t::on_recv( uint32_t evts, hude::any_t& )
        {
            // llog_dug( "<plugs>", "\t BEGIN: Aio event of gpio: <evts: %#x>", evts );

            if( 0U != ( evts & hude::aio::evt_type_t::emEPOLLIN ) )
            {
                size_t count = 0;

                do
                {
                    hude::buffer_t buffer( 1024U );

                    auto ret = sport_.recv( &(buffer[0]), buffer.size() );

                    if( ret > 0 )
                    {
                        buffer.resize( ret );

                        llog_tra( "<plugs>", "\t\t <<< Recv buffer[ %s ]", hude::utils::to_hex( buffer ).c_str() );

                        parses_.buffer_push( buffer );

                        count += ret;
                    } else {
                        // if( ret == 0 ) llog_war( "<plugs>", "\t Event of aio: recv() == %d.", ret );

                        break;
                    }
                } while ( false );
                
                // 在异步任务池执行缓冲区数据解析
                if( count ) aio_->task_commit( [=]{ this->parse(); } );
            }

            if( 0U != ( evts & hude::aio::evt_type_t::emEPOLLHUP ) )
            {
                llog_nte( "<plugs>", "\t Remote disconnected." );
            }

            if( 0U != ( evts & hude::aio::evt_type_t::emEPOLLERR ) )
            {
                llog_war( "<plugs>", "\t Error event." );

                sport_.close();
            }

            // llog_dug( "<plugs>", "\t END: Aio event of gpio: <evts: %#x>", evts );
        }

        /*********************************************************************
         * @brief 初始化
         */
        inline bool gpio_client_t::init()
        {
            llog_tra( "<plugs>", "\t virt-gpio init." );

            {
                if( name_.empty() || speed_ <= 0 ) return false;

                // 打开串口
                auto ret = ( sport_.open( name_.c_str(), false ) && sport_.settings( speed_ ) );

                if( ! ret )
                {
                    llog_err( "<plugs>", "ERROR: open tty." );
                    return false;
                }
            }

            {
                namespace pl = std::placeholders;

                // 订阅串口的异步IO事件
                auto ret = aio_->evt_commit( sport_.native(),
                    std::bind( &gpio_client_t::on_recv, this, std::placeholders::_1, std::placeholders::_2 ) );

                if( ret )
                {
                    llog_err( "<plugs>", "ERROR: commit event handle to epoll, aio::evt_commit() == %d", ret );
                    return false;
                }
            }

            return true;
        }

        /*********************************************************************
         * @brief 反初始化
         */
        inline void gpio_client_t::deinit()
        {
            aio_->evt_remove( sport_.native() );

            sport_.close();

            llog_tra( "<plugs>", "\t virt-gpio deinit." );
        }

        /*********************************************************************
         * @brief 发送数据桢
         */
        inline bool gpio_client_t::send( const hude::buffer_t& buffer )
        {
            llog_tra( "<plugs>", "\t\t >>> Send buffer[ %s ]", hude::utils::to_hex( buffer ).c_str() );

            auto ret = sport_.send( &(buffer[0]), buffer.size() );
            return (size_t)ret == buffer.size();
        }

        /*********************************************************************
         * @brief 解析缓冲中存在的所有数据
         */
        inline void gpio_client_t::parse()
        {
            // 无效数据丢弃事件
            auto drop = []( parses_t::citr_t b, parses_t::citr_t e ) -> void
            {
                llog_war( "<plugs>", "\t\t droped bytes: %u(%s)", e - b, hude::utils::to_hex( &(*b), e - b ).c_str() );
            };

            if( ! parsing_.test_and_set() )
            {
                parses_.parse_all( &pkg_gpio_t::scaner, drop );

                parsing_.clear();
            }
        }

        /*********************************************************************
         * @brief 完成一次同步请求与回应
         */
        inline bool gpio_client_t::request( const pkg_gpio_t& req, std::function< void( pkg_gpio_t& ) > cb, size_t timeout )
        {
            auto           p = ( const uint8_t* )&req;
            hude::buffer_t buffer{ p, p + pkg_gpio_t::emSIZ_PKG };
            sync_t         sync( this, &parses_ );

            auto ret = sync.request( buffer, [=]( parses_t::citr_t b, parses_t::citr_t e ) -> sync_t::result_t
            {
                if( b == e ) return sync_t::emRET_UNKNOWN;

                auto pkg = (pkg_gpio_t*)&(*b);

                cb( *pkg );

                return sync_t::emRET_SUCCESS;
            }, timeout, true );

            return ret == sync_t::emRET_SUCCESS;
        }

        /*********************************************************************
         * @brief 查询指定gpio是否已经打开
         */
        inline hude::optional_t< bool > gpio_client_t::enable( uint16_t index )
        {
            pkg_gpio_t req( 0U, index, pkg_gpio_t::value_t::emVL,
                pkg_gpio_t::direction_t::emDIN, pkg_gpio_t::cmd_t::emCMD_OPENED );

            bool ret = false;

            auto err = this->request( req, [&]( pkg_gpio_t& pkg ) {
                ret = ( !! pkg.gpio.flags );
            }, 500UL );

            if( err )
                return ret;
            else
                return hude::optional_null_t{};
        }

        /*********************************************************************
         * @brief 打开指定gpio
         */
        inline bool gpio_client_t::enable( uint16_t index, bool value )
        {
            auto v = value? pkg_gpio_t::cmd_t::emCMD_OPEN : pkg_gpio_t::cmd_t::emCMD_CLOSE;

            pkg_gpio_t req( 0U, index, pkg_gpio_t::value_t::emVL,
                pkg_gpio_t::direction_t::emDIN, v );

            bool ret = false;

            auto err = this->request( req, [&]( pkg_gpio_t& pkg ) {
                ret = ( ! pkg.gpio.result );
            }, 500UL );

            return ( err && ret );
        }

        /*********************************************************************
         * @brief 设置指定gpio的方向
         */
        inline bool gpio_client_t::direction( uint16_t index, dir_t dir )
        {
            pkg_gpio_t req( 0U, index, pkg_gpio_t::value_t::emVL,
                dir, pkg_gpio_t::cmd_t::emCMD_SET_DIR );

            bool ret = false;

            auto err = this->request( req, [&]( pkg_gpio_t& pkg ) {
                ret = ( ! pkg.gpio.result );
            }, 500UL );

            return ( err && ret );
        }

        /*********************************************************************
         * @brief 查询指定gpio的方向
         */
        inline hude::optional_t< gpio_client_t::dir_t > gpio_client_t::direction( uint16_t index )
        {
            pkg_gpio_t req( 0U, index, pkg_gpio_t::value_t::emVL,
                pkg_gpio_t::direction_t::emDIN, pkg_gpio_t::cmd_t::emCMD_GET_DIR );

            bool ret = false;
            bool val = false;

            auto err = this->request( req, [&]( pkg_gpio_t& pkg ) {
                ret = ( !  pkg.gpio.result );
                val = ( !! pkg.gpio.direction );
            }, 500UL );

            if( err )
                return val? dir_t::emDOUT : dir_t::emDIN;
            else
                return hude::optional_null_t{};
        }

        /*********************************************************************
         * @brief 查询指定gpio的电平
         */
        inline hude::optional_t< bool > gpio_client_t::value( uint16_t index )
        {
            pkg_gpio_t req( 0U, index, pkg_gpio_t::value_t::emVL,
                pkg_gpio_t::direction_t::emDIN, pkg_gpio_t::cmd_t::emCMD_GET );

            bool ret = false;
            bool val = false;

            auto err = this->request( req, [&]( pkg_gpio_t& pkg ) {
                ret = ( !  pkg.gpio.result );
                val = ( !! pkg.gpio.value );
            }, 500UL );

            if( err && ret )
                return val;
            else
                return hude::optional_null_t{};
        }

        /*********************************************************************
         * @brief 设置指定gpio的电平
         */
        inline bool gpio_client_t::value( uint16_t index, bool level )
        {
            auto val = level? pkg_gpio_t::value_t::emVH : pkg_gpio_t::value_t::emVL;

            pkg_gpio_t req( 0U, index, val,
                dir_t::emDIN, pkg_gpio_t::cmd_t::emCMD_SET );

            bool ret = false;

            auto err = this->request( req, [&]( pkg_gpio_t& pkg ) {
                ret = ( ! pkg.gpio.result );
            }, 500UL );

            return ( err && ret );
        }

    } //namespace brick
} //namespace ha

#endif //__virt_gpio_kenny_dkfjiwoo3w3__


